home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / listings / v_11_12 / winroth / exc_err.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-20  |  24.0 KB  |  1,083 lines

  1. /*
  2. ** Exception Library -- General exception handling for ANSI C programs
  3. ** 
  4. ** Copyright (C) 1992 Computational Vision and Active Perception Lab. (CVAP),
  5. **                    Royal Institute of Technology, Stockholm.
  6. **
  7. ** This library is free software; you can redistribute it and/or
  8. ** modify it under the terms of the GNU Library General Public
  9. ** License as published by the Free Software Foundation; either
  10. ** version 2 of the License, or (at your option) any later version.
  11. ** 
  12. ** This library is distributed in the hope that it will be useful,
  13. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15. ** Library General Public License for more details.
  16. ** 
  17. ** You should have received a copy of the GNU Library General Public
  18. ** License along with this library (see COPYING-LIB); if not, write to 
  19. ** the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 
  20. ** USA.
  21. **
  22. **                            Written by
  23. **
  24. **                   Harald Winroth, Matti Rendahl
  25. **         Computational Vision and Active Perception Laboratory
  26. **            Royal Institute of Technology
  27. **              S-100 44 Stockholm
  28. **                Sweden
  29. **
  30. ** Report bugs to candela-bug@bion.kth.se, and direct all inquiries to 
  31. ** candela@bion.kth.se.
  32. **
  33. */
  34.  
  35. #include <stdlib.h>
  36. #include <stdio.h>
  37. #include <string.h>
  38. #include <errno.h>
  39. #include <sys/ioctl.h>
  40.  
  41. #include <exception/exception.h>
  42. #include <exception/exc-err.h>
  43.  
  44. /* sprintf buffers */
  45. #define TEXT_BUF_SIZE            (4 * 1024)
  46. #define TEXT_BUF_CHECK_SUM     0x81828384
  47. #define EMPTY_TEXT_BUF           {"", TEXT_BUF_CHECK_SUM}
  48. typedef struct 
  49. {
  50.     char str[TEXT_BUF_SIZE];
  51.     unsigned int check_sum;
  52.  
  53. } textBuf;
  54.  
  55. /* Private functions */
  56. static excErrFormat* append_format (excErrBuf *buf, excErrFormatTag tag);
  57. static void make_buf_space (excErrBuf *buf, unsigned int req_len);
  58. #ifdef HAVE_TERMCAP
  59. static void append_str (excErrBuf *buf, const char *s);
  60. #endif
  61. static void append_nstr (excErrBuf *buf, const char *s, unsigned int n);
  62. static void tab_to_cur (excErrBuf *buf);
  63. static void new_line (excErrBuf *buf);
  64. static void soft_new_line (excErrBuf *buf);
  65. static int is_left_delim (excErrBuf *buf, char c);
  66. static int is_right_delim (excErrBuf *buf, char c);
  67. static int is_white (excErrBuf *buf, char c);
  68. static void check_text_buf (textBuf *buf);
  69.  
  70. /* Place-holder representing the excErr type */
  71. excErrType exc_err_type;
  72.  
  73. /* 
  74.  * exc_err_progname contains the program name if set, or "anonymous program". 
  75.  * It is set if exc_err_progname != exc_err_anonymous_program.
  76.  */
  77.  
  78. char exc_err_anonymous_program[] = "anonymous-program";
  79. char *exc_err_progname = exc_err_anonymous_program;
  80.  
  81. /* 
  82.  * Access functions for excErr exception variables
  83.  */
  84.  
  85. excErr *exc_err_domain (excErr *e)
  86. {
  87.     return e->in_domain;
  88. }
  89.  
  90. char *exc_err_name (excErr *e)
  91. {
  92.     return e->name;
  93. }
  94.  
  95. char *exc_err_str (excErr *e)
  96. {
  97.     return e->str;
  98. }
  99.  
  100. excErr *exc_err_top_domain (excErr *e)
  101. {
  102.     while (exc_err_domain (e))
  103.     e = exc_err_domain (e);
  104.     
  105.     return e;
  106. }
  107.  
  108. char *exc_err_package_name (excErr *e)
  109. {
  110.     /* 
  111.       The package name can be found in the 'str' field of the top-level domain.
  112.       If no package name has been specified, use the top-level domain name.
  113.     */
  114.     char *name = exc_err_str (exc_err_top_domain (e));
  115.     
  116.     if (!name)
  117.     name = exc_err_name (exc_err_top_domain (e));
  118.  
  119.     return name;
  120. }
  121.  
  122. static void build_full_name (excErr *e, char *buf, unsigned int *len, 
  123.                  unsigned int buf_size)
  124. {
  125.     char *s;
  126.     unsigned int n;
  127.  
  128.     if (!e) 
  129.     return;
  130.  
  131.     build_full_name (exc_err_domain (e), buf, len, buf_size);
  132.  
  133.     s = exc_err_name (e) ? exc_err_name (e) : "<noname>";
  134.     n = strlen (s);
  135.  
  136.     if (*len + n + 2 > buf_size)
  137.     exc_fatal ("exc_err_full_name: buffer overflow");
  138.     
  139.     strcpy (buf + *len, s);
  140.     strcpy (buf + *len + n, ".");
  141.     *len += n+1;
  142. }
  143.  
  144. char *exc_err_full_name (excErr *e)
  145. {
  146.     static textBuf text_buf = EMPTY_TEXT_BUF;
  147.     unsigned int len = 0;
  148.  
  149.     build_full_name (e, &text_buf.str[0], &len, TEXT_BUF_SIZE);
  150.  
  151.     text_buf.str[len-1] = '\0'; /* Suppress the last '.' */
  152.     return &text_buf.str[0];
  153. }
  154.  
  155. /* 
  156.  * Creating, destroying and clearing error message buffers
  157.  */
  158.  
  159. static void init_buf (excErrBuf *buf, char *left_delim, char *right_delim, 
  160.               char *white,  char *auto_break_char, unsigned int width, 
  161.               unsigned int max_tab)
  162. {
  163.     if (width < 1)
  164.     width = 1;
  165.  
  166.     if (max_tab > width-1)
  167.     max_tab = width-1;
  168.  
  169.     if (!left_delim)
  170.     left_delim = "";
  171.  
  172.     if (!right_delim)
  173.     right_delim = "";
  174.  
  175.     if (!white)
  176.     white = " \t";
  177.  
  178.     if (!auto_break_char || !*auto_break_char)
  179.     auto_break_char = "\\";
  180.  
  181.     buf->width = width;
  182.     buf->max_tab = max_tab;
  183.     buf->left_delim = strcpy (exc_malloc (strlen (left_delim) + 1), 
  184.                   left_delim);
  185.     buf->right_delim = strcpy (exc_malloc (strlen (right_delim) + 1), 
  186.                    right_delim);
  187.     buf->whitespace = strcpy (exc_malloc (strlen (white) + 1), white);
  188.     buf->auto_break_char = strcpy (exc_malloc (strlen (auto_break_char) + 1), 
  189.                    auto_break_char);
  190.  
  191.     buf->str = NULL;
  192.     buf->str_len = 0;
  193.     buf->str_pos = 0;
  194.  
  195.     buf->cur_tab = 0;
  196.     buf->cur_col = 0;
  197.     
  198.     buf->format = NULL;
  199.     buf->format_len = 0;
  200.     buf->format_pos = 0;
  201. }
  202.  
  203. static void destroy_buf (excErrBuf *buf)
  204. {
  205.     unsigned int i;
  206.  
  207.     exc_free (buf->left_delim);
  208.     exc_free (buf->right_delim);
  209.     exc_free (buf->whitespace);
  210.     exc_free (buf->auto_break_char);
  211.  
  212.     for (i=0; i<buf->format_pos; i++)
  213.     if (buf->format[i].tag == excErrStr)
  214.         exc_free (buf->format[i].arg.str);
  215.      
  216.     exc_free (buf->str);
  217.     exc_free (buf->format);
  218. }
  219.  
  220. excErrBuf *exc_err_buf_new (excErrBufArg first, ...)
  221. {
  222.     excErrBuf *buf = (excErrBuf *) exc_malloc (sizeof (excErrBuf));
  223.     char *left_delim = NULL;
  224.     char *right_delim = NULL;
  225.     char *white = NULL;
  226.     char *auto_break_char = "\\";
  227.     unsigned int width = 80;
  228.     unsigned int max_tab = 60;
  229.     va_list rest;
  230.  
  231.     va_start (rest, first);
  232.     while (first)
  233.     {
  234.     switch (first)
  235.     {
  236.     case excErrBufArgLeftDelimiters:
  237.         left_delim = va_arg (rest, char*);
  238.         break;
  239.     
  240.     case excErrBufArgRightDelimiters:
  241.         right_delim = va_arg (rest, char*);
  242.         break;
  243.     
  244.     case excErrBufArgWhitespace:
  245.         white = va_arg (rest, char*);
  246.         break;
  247.     
  248.     case excErrBufArgWidth:
  249.         width = va_arg (rest, unsigned int);
  250.         break;
  251.     
  252.     case excErrBufArgMaxTab:
  253.         max_tab = va_arg (rest, unsigned int);
  254.         break;
  255.     
  256.     case excErrBufArgAutoBreakChar:
  257.         auto_break_char = va_arg (rest, char*);
  258.         break;
  259.  
  260.     default:
  261.         break; /* skip unrecognized key/value pair */
  262.     }
  263.  
  264.     first = va_arg (rest, excErrBufArg);
  265.     }
  266.  
  267.     init_buf (buf, left_delim, right_delim, white, auto_break_char, width, 
  268.           max_tab);
  269.  
  270.     va_end (rest);
  271.     return buf;
  272. }
  273.  
  274. void exc_err_buf_delete (excErrBuf *buf)
  275. {
  276.     destroy_buf (buf);
  277.     exc_free (buf);
  278. }
  279.  
  280. void exc_err_buf_clear (excErrBuf *buf)
  281. {
  282.     unsigned int i;
  283.  
  284.     for (i=0; i<buf->format_pos; i++)
  285.     if (buf->format[i].tag == excErrStr)
  286.         exc_free (buf->format[i].arg.str);
  287.      
  288.     exc_free (buf->str);
  289.     exc_free (buf->format);
  290.  
  291.     buf->str = NULL;
  292.     buf->str_len = 0;
  293.     buf->str_pos = 0;
  294.  
  295.     buf->cur_tab = 0;
  296.     buf->cur_col = 0;
  297.     
  298.     buf->format = NULL;
  299.     buf->format_len = 0;
  300.     buf->format_pos = 0;
  301. }
  302.  
  303. /*
  304.  * Video attributes (if termcap is available)
  305.  */
  306.  
  307. static int format_is_on[excErrNumFormatTags] = {0}; /* boolean */
  308.  
  309. #ifdef HAVE_TERMCAP
  310.  
  311. extern int tgetent();
  312. extern char *tgetstr();
  313.  
  314. static char *termcap_name[excErrNumFormatTags][2 /* {off, on} */] =
  315. {
  316.     /* underline */ {"ue", "us"},
  317.     /* inverse */   {"me"/* ?? */, "mr"},
  318.     /* stand out */ {"se", "so"}
  319.     /* all additional (non-termcap) fields are set to NULL */
  320. };
  321.  
  322. static int termcap_failed = 0;
  323.  
  324. static char *termcap_str (excErrBuf *buf, char *cap)
  325. {
  326.     static char str_buf[1024];
  327.     char *str_buf_ptr = &str_buf[0];
  328.     char *str;
  329.  
  330.     if (termcap_failed)
  331.     return NULL;
  332.  
  333.     str = tgetstr (cap, &str_buf_ptr); 
  334.     *str_buf_ptr++ = '\0';
  335.  
  336.     return str; /* might be NULL */
  337. }
  338.  
  339. static void clear_all_attr (excErrBuf *buf)
  340. {
  341.     int i;
  342.  
  343.     for (i=0; i<excErrNumFormatTags; i++)
  344.     if (termcap_name[i][0] != NULL)
  345.         append_str (buf, termcap_str (buf, termcap_name[i][0]));
  346. }
  347.  
  348. static void reset_all_attr (excErrBuf *buf)
  349. {
  350.     int i;
  351.  
  352.     clear_all_attr (buf);
  353.  
  354.     for (i=0; i<excErrNumFormatTags; i++)
  355.     if (format_is_on[i] && termcap_name[i][1] != NULL)
  356.         append_str (buf, termcap_str (buf, termcap_name[i][1]));
  357. }
  358.  
  359. static void set_attr (excErrBuf *buf, excErrFormatTag attr, int on)
  360. {
  361.     format_is_on[attr] = on;
  362.     reset_all_attr (buf);
  363. }
  364.  
  365. #else
  366.  
  367. static void clear_all_attr (excErrBuf *buf)
  368. {
  369.     ; /* no-op */
  370. }
  371.  
  372. static void reset_all_attr (excErrBuf *buf)
  373. {
  374.     ; /* no-op */
  375. }
  376.  
  377. static void set_attr (excErrBuf *buf, excErrFormatTag attr, int on)
  378. {
  379.     format_is_on[attr] = on;
  380. }
  381.  
  382. #endif /* HAVE_TERMCAP */
  383.  
  384.  
  385. /* 
  386.  * Primitive formatting calls for error message buffers 
  387.  */
  388.  
  389. static char *handle_newlines (excErrBuf *buf, char *str)
  390. {
  391.     if (*str == '\n')
  392.     {
  393.     new_line (buf);
  394.     str++;
  395.     return str;
  396.     }
  397.  
  398.     return NULL;
  399. }
  400.  
  401. static char *whole_words_on_current_line (excErrBuf *buf, char *str,
  402.                       int indivisible)
  403. {
  404.     int n = buf->width - buf->cur_col; /* n >= 0 */
  405.     int i;
  406.  
  407.     for (i=0; i<n; i++)
  408.     if ((is_white (buf, str[i]) && !indivisible) || 
  409.         (is_right_delim (buf, str[i]) && !indivisible) ||
  410.         (is_white (buf, str[i+1]) && !indivisible) || 
  411.         (is_left_delim (buf, str[i+1]) && !indivisible) ||
  412.         str[i+1]=='\n' || str[i+1]=='\0')
  413.     {
  414.         append_nstr (buf, str, i+1);
  415.         buf->cur_col += i+1;
  416.         str += i+1;
  417.         return str;
  418.     }
  419.     
  420.     return NULL;
  421. }
  422.  
  423. static char *whole_words_on_next_line (excErrBuf *buf, char *str, 
  424.                        int indivisible)
  425. {
  426.     int n = buf->width - buf->cur_tab; /* n >= 0 */
  427.     int nwhites, i;
  428.  
  429.     for (nwhites=0; is_white (buf, str[nwhites]); nwhites++);
  430.     if (nwhites > 0) /* supress whitespaces after auto-newline */
  431.     {
  432.     soft_new_line (buf);
  433.     str += nwhites;
  434.     return str;
  435.     }
  436.  
  437.     /* First char in str is not whitespace here */
  438.     for (i=0; i<n; i++)
  439.     if ((is_white (buf, str[i]) && !indivisible) || 
  440.         (is_right_delim (buf, str[i]) && !indivisible) ||
  441.         (is_white (buf, str[i+1]) && !indivisible) || 
  442.         (is_left_delim (buf, str[i+1]) && !indivisible) ||
  443.         str[i+1]=='\n' || str[i+1]=='\0')
  444.     {
  445.         soft_new_line (buf);
  446.         append_nstr (buf, str, i+1);
  447.         buf->cur_col += i+1;
  448.         str += i+1;
  449.         return str;
  450.     }
  451.     
  452.     return NULL;
  453. }
  454.  
  455. static char *chars_on_current_line (excErrBuf *buf, char *str)
  456. {
  457.     int n = buf->width - buf->cur_col; /* n >= 0 */
  458.     int max;
  459.  
  460.     if (n>1)
  461.     {
  462.     max = strlen (str);
  463.     if (n-1 > max)
  464.         n = max+1;
  465.  
  466.     append_nstr (buf, str, n-1);
  467.     append_nstr (buf, buf->auto_break_char, 1);
  468.  
  469.     buf->cur_col += n;
  470.     str += n-1;
  471.     return str;
  472.     }
  473.  
  474.     return NULL;
  475. }
  476.  
  477. static char *chars_on_next_line (excErrBuf *buf, char *str)
  478. {
  479.     int n = buf->width - buf->cur_tab; /* n >= 0 */
  480.     int nwhites, max;
  481.  
  482.     if (n>1)
  483.     {
  484.     soft_new_line (buf);
  485.     for (nwhites=0; is_white (buf, str[nwhites]); nwhites++);
  486.  
  487.     max = strlen (str+nwhites);
  488.     if (n-1 > max)
  489.         n = max+1;
  490.  
  491.     append_nstr (buf, str+nwhites, n-1);
  492.     append_nstr (buf, buf->auto_break_char, 1);
  493.  
  494.     buf->cur_col += n;
  495.     str += nwhites+n-1;
  496.     return str;
  497.     }
  498.     
  499.     return NULL;
  500. }
  501.  
  502. static char *force_chars_on_current_line (excErrBuf *buf, char *str)
  503. {
  504.     if (*str)
  505.     {
  506.     append_nstr (buf, str, 1);
  507.     buf->cur_col += 1;
  508.     str += 1;
  509.     }
  510.  
  511.     return str; /* always != NULL */
  512. }
  513.  
  514. static void buf_print (excErrBuf *buf, const char *input_str, int indivisible)
  515. {
  516.     char *str, *next;
  517.     
  518.     if (!input_str)
  519.     return;
  520.  
  521.     str = strcpy (exc_malloc (strlen (input_str) + 1), input_str);
  522.     append_format (buf, excErrStr)->arg.str = str;
  523.  
  524.     for ( ; *str ; str=next)
  525.     {
  526.     /*
  527.       Handle explicit newlines
  528.     */
  529.     next = handle_newlines (buf, str);
  530.     if (next) continue;
  531.  
  532.     /* 
  533.       Try to fit a complete word into the current line 
  534.     */
  535.     next = whole_words_on_current_line (buf, str, indivisible);
  536.     if (next) continue;
  537.         
  538.     /* 
  539.       Check to see if a complete word can fit into line after an 
  540.       automatic line break.
  541.     */
  542.     next = whole_words_on_next_line (buf, str, indivisible);
  543.     if (next) continue;
  544.  
  545.     /*
  546.       There is no space for complete words. Try to squeze in as much as 
  547.       possible in the current line (without auto line break).      
  548.     */
  549.     next = chars_on_current_line (buf, str);
  550.     if (next) continue;
  551.  
  552.     /*
  553.       Make an automatic linebreak and try to squeze in as much as possible
  554.       on that line (after tabulation).
  555.     */
  556.     next = chars_on_next_line (buf, str);
  557.     if (next) continue;
  558.  
  559.     next = force_chars_on_current_line (buf, str); /* panic */
  560.     }
  561.  
  562.     buf->str[buf->str_pos] = '\0'; /* There is always space enough for this */
  563. }
  564.  
  565. void exc_err_buf_print (excErrBuf *buf, const char *input_str)
  566. {
  567.     buf_print (buf, input_str, /* indivisible */ 0);
  568. }
  569.  
  570. void exc_err_buf_print_indivis (excErrBuf *buf, const char *input_str)
  571. {
  572.     buf_print (buf, input_str, /* indivisible */ 1);
  573. }
  574.  
  575. void exc_err_buf_vprintf (excErrBuf *buf, const char *fmt, va_list l)
  576. {
  577.     static textBuf text_buf = EMPTY_TEXT_BUF;
  578.     
  579.     vsprintf (&text_buf.str[0], fmt, l);
  580.     check_text_buf (&text_buf);
  581.  
  582.     exc_err_buf_print (buf, &text_buf.str[0]);
  583. }
  584.  
  585. void exc_err_buf_vprintf_indivis (excErrBuf *buf, const char *fmt, va_list l)
  586. {
  587.     static textBuf text_buf = EMPTY_TEXT_BUF;
  588.     
  589.     vsprintf (&text_buf.str[0], fmt, l);
  590.     check_text_buf (&text_buf);
  591.  
  592.     exc_err_buf_print_indivis (buf, &text_buf.str[0]);
  593. }
  594.  
  595. void exc_err_buf_printf (excErrBuf *buf, const char *fmt, ...)
  596. {
  597.     va_list l;
  598.  
  599.     va_start (l, fmt);
  600.     exc_err_buf_vprintf (buf, fmt, l);
  601.     va_end (l);
  602. }
  603.  
  604. void exc_err_buf_printf_indivis (excErrBuf *buf, const char *fmt, ...)
  605. {
  606.     va_list l;
  607.  
  608.     va_start (l, fmt);
  609.     exc_err_buf_vprintf_indivis (buf, fmt, l);
  610.     va_end (l);
  611. }
  612.  
  613. void exc_err_buf_print_buf (excErrBuf *buf, excErrBuf *src)
  614. {
  615.     unsigned int i;
  616.  
  617.     for (i=0; i<src->format_pos; i++)
  618.     switch (src->format[i].tag)
  619.     {
  620.     case excErrStr:
  621.         exc_err_buf_print (buf, src->format[i].arg.str);
  622.         break;
  623.  
  624.     case excErrTab:
  625.         exc_err_buf_tab (buf, src->format[i].arg.col);
  626.         break;
  627.  
  628.     case excErrUnderline:
  629.         exc_err_buf_underline (buf, src->format[i].arg.on);
  630.         break;
  631.  
  632.     case excErrInverse:
  633.         exc_err_buf_inverse (buf, src->format[i].arg.on);
  634.         break;
  635.  
  636.     case excErrStandOut:
  637.         exc_err_buf_stand_out (buf, src->format[i].arg.on);
  638.         break;
  639.  
  640.     default:
  641.         exc_fatal ("exc_err_buf_print_buf: unknown format tag");
  642.     }
  643. }
  644.  
  645. void exc_err_buf_tab (excErrBuf *buf, unsigned int col)
  646. {
  647.     append_format (buf, excErrTab)->arg.col = col;
  648.  
  649.     if (col > buf->max_tab)
  650.     {
  651.     buf->cur_tab = buf->max_tab;
  652.     if (buf->cur_tab < buf->cur_col+1)
  653.         soft_new_line (buf);
  654.     }
  655.     else
  656.     buf->cur_tab = col;
  657.  
  658.     if (buf->cur_tab< buf->cur_col)
  659.     soft_new_line (buf); /* will tab to current tab column */
  660.     else
  661.     tab_to_cur (buf);
  662. }
  663.  
  664. void exc_err_buf_underline (excErrBuf *buf, int on)
  665. {
  666.     append_format (buf, excErrUnderline)->arg.on = on;
  667.     set_attr (buf, excErrUnderline, on);
  668. }
  669.  
  670. void exc_err_buf_inverse (excErrBuf *buf, int on)
  671. {
  672.     append_format (buf, excErrInverse)->arg.on = on;
  673.     set_attr (buf, excErrInverse, on);
  674. }
  675.  
  676. void exc_err_buf_stand_out (excErrBuf *buf, int on)
  677. {
  678.     append_format (buf, excErrStandOut)->arg.on= on;
  679.     set_attr (buf, excErrStandOut, on);
  680. }
  681.  
  682. /* 
  683.  * Access functions for excErrBuf 
  684.  */
  685.  
  686. char *exc_err_buf_str (excErrBuf *buf)
  687. {
  688.     return buf->str;
  689. }
  690.  
  691. /* 
  692.  * Global error message buffer 
  693.  */
  694.  
  695. excErrBuf *exc_err_buf;
  696.  
  697. /* 
  698.  * exc_err_buf_*() calls on the global exc_err_buf buffer (syntatic sugar)
  699.  */
  700.  
  701. void exc_err_print (const char *str)
  702. {
  703.     exc_err_init ();
  704.     exc_err_buf_print (exc_err_buf, str);
  705. }
  706.  
  707. void exc_err_print_indivis (const char *str)
  708. {
  709.     exc_err_init ();
  710.     exc_err_buf_print_indivis (exc_err_buf, str);
  711. }
  712.  
  713. void exc_err_printf (const char *fmt, ...)
  714. {
  715.     va_list l;
  716.  
  717.     va_start (l, fmt);
  718.     exc_err_init ();
  719.     exc_err_buf_vprintf (exc_err_buf, fmt, l);
  720.     va_end (l);
  721. }
  722.  
  723. void exc_err_printf_indivis (const char *fmt, ...)
  724. {
  725.     va_list l;
  726.  
  727.     va_start (l, fmt);
  728.     exc_err_init ();
  729.     exc_err_buf_vprintf_indivis (exc_err_buf, fmt, l);
  730.     va_end (l);
  731. }
  732.  
  733. void exc_err_vprintf (const char *fmt, va_list l)
  734. {
  735.     exc_err_init ();
  736.     exc_err_buf_vprintf (exc_err_buf, fmt, l);
  737. }
  738.  
  739. void exc_err_vprintf_indivis (const char *fmt, va_list l)
  740. {
  741.     exc_err_init ();
  742.     exc_err_buf_vprintf_indivis (exc_err_buf, fmt, l);
  743. }
  744.  
  745. void exc_err_tab (unsigned int col)
  746. {
  747.     exc_err_init ();
  748.     exc_err_buf_tab (exc_err_buf, col);
  749. }
  750.  
  751. void exc_err_underline (int on)
  752. {
  753.     exc_err_init ();
  754.     exc_err_buf_underline (exc_err_buf, on);
  755. }
  756.  
  757. void exc_err_inverse (int on)
  758. {
  759.     exc_err_init ();
  760.     exc_err_buf_inverse (exc_err_buf, on);
  761. }
  762.  
  763. void exc_err_stand_out (int on)
  764. {
  765.     exc_err_init ();
  766.     exc_err_buf_stand_out (exc_err_buf, on);
  767. }
  768.  
  769. void exc_err_clear (void)
  770. {
  771.     exc_err_init ();
  772.     exc_err_buf_clear (exc_err_buf);
  773. }
  774.  
  775. /*
  776.  * Throwing an excErr (adds no text to the error message buffer). 
  777.  *
  778.  * When exc_err_throw_fatal is used, handler will exit with status 1.
  779.  * When exc_err_throw_nonfatal is used, handler will exit with status 0.
  780.  */
  781.  
  782. void exc_err_throw_fatal (excErr e)
  783. {
  784.     exc_throw_typed (e.self, &exc_err_type.fatal);
  785. }
  786.     
  787. void exc_err_throw_nonfatal (excErr e)
  788. {
  789.     exc_throw_typed (e.self, &exc_err_type.nonfatal);
  790. }
  791.     
  792. /* 
  793.  * Single line error messages 
  794.  */
  795.  
  796. static void print_error_header (excErr e)
  797. {
  798.     if (exc_err_progname != exc_err_anonymous_program)
  799.     exc_err_printf ("%s: ", exc_err_progname);
  800.  
  801.     exc_err_printf ("%s\n", exc_err_full_name (e.self));
  802. }
  803.  
  804. void exc_error (excErr e, const char *fmt, ...)
  805. {
  806.     va_list l;
  807.  
  808.     va_start (l, fmt);
  809.     exc_verror (e, fmt, l);
  810.     va_end (l); /* Not reached--exc_verror() throws an exception */
  811. }
  812.  
  813. void exc_verror (excErr e, const char *fmt, va_list l)
  814. {
  815.     exc_err_init ();
  816.     print_error_header (e);
  817.  
  818.     if (fmt)
  819.     exc_err_vprintf (fmt, l);
  820.     else
  821.     exc_err_print ((e.str) ? e.str : "undocumented error");
  822.  
  823.     exc_err_throw_fatal (e);
  824. }
  825.  
  826. void exc_perror (excErr e, const char *fmt, ...)
  827. {
  828.     va_list l;
  829.  
  830.     va_start (l, fmt);
  831.     exc_vperror (e, fmt, l);
  832.     va_end (l); /* Not reached--exc_vperror() throws an exception */
  833. }
  834.  
  835. void exc_vperror (excErr e, const char *fmt, va_list l)
  836. {
  837.     char *str = strerror (errno);
  838.  
  839.     exc_err_init ();
  840.     print_error_header (e);
  841.  
  842.     if (fmt)
  843.     exc_err_vprintf (fmt, l);
  844.     
  845.     if (str)
  846.     exc_err_print (str);
  847.     else
  848.     exc_err_printf ("undocumented system error (%d)", errno);
  849.     
  850.     exc_err_throw_fatal (e);
  851. }
  852.  
  853. /* 
  854.  * Displaying messages (used by exc_err_handler) 
  855.  */
  856.  
  857. void (*exc_err_print_hook)(const char *str) = exc_err_builtin_print;
  858.  
  859. void exc_err_builtin_print (const char *str)
  860. {
  861.     fprintf (stderr, "\n%s\n", str);
  862. }
  863.  
  864. /* Callback for clearing message buffer */
  865.  
  866. static void cb (excCallbackTag tag, void *cb_data, void **try_data)
  867. {
  868.     if (tag == excRecoverCallback)
  869.     exc_err_clear ();
  870. }
  871.  
  872. /* Default exception handler */
  873.  
  874. int exc_err_handler (void *e, void *e_type, void *h_data)
  875. {
  876.     excErr *ee;
  877.  
  878.     if (!EXC_IN_DOMAIN (e_type, exc_err_type))
  879.     exc_rethrow ();
  880.  
  881.     ee = (excErr *) e;
  882.  
  883.     if (!exc_err_buf_str (exc_err_buf) || !*exc_err_buf_str (exc_err_buf))
  884.     {
  885.     print_error_header (*ee);
  886.     exc_err_printf ("%s\n", (ee->str) ? ee->str : "undocumented error");
  887.     }
  888.  
  889.     (*exc_err_print_hook) (exc_err_buf_str (exc_err_buf));
  890.  
  891.     return EXC_IN_DOMAIN (e_type, exc_err_type.fatal) ? 1 : 0;
  892. }
  893.  
  894. /* 
  895.  * Initialization 
  896.  */
  897.  
  898. #define EXC_ERR_DEFAULT_WIDTH 80
  899.  
  900. static int get_tty_width (void)
  901. {
  902. #if defined(SVR4) || defined(SYSV) || defined(_SYSV_) 
  903.   return EXC_ERR_DEFAULT_WIDTH;
  904. #else
  905.     struct winsize w;
  906.  
  907.     if (ioctl (fileno (stderr), TIOCGWINSZ, &w))
  908.     return EXC_ERR_DEFAULT_WIDTH; /* ioctl() failed */
  909.     else if (w.ws_col <= 0)
  910.     return EXC_ERR_DEFAULT_WIDTH;
  911.     else
  912.     return (int) (w.ws_col);
  913. #endif
  914. }
  915.  
  916. void exc_err_init (void)
  917. {
  918. #ifdef HAVE_TERMCAP
  919.     static char *str_buf[1024];
  920.     char *term_type;
  921. #endif /* HAVE_TERMCAP */
  922.  
  923.     static int exc_err_inited = 0;
  924.     unsigned min_chars = 20;
  925.     unsigned int max_tab = 0;
  926.     unsigned int width;
  927.  
  928.     if (exc_err_inited) 
  929.     return;
  930.  
  931.     width = get_tty_width ();
  932.  
  933.     /* Make sure that at least 'min_chars' chars can be printed on each line */
  934.     if (min_chars < width)
  935.     max_tab = width-min_chars;
  936.     else
  937.     max_tab = 0;
  938.  
  939.     exc_err_buf = exc_err_buf_new (excErrBufArgWidth, width, 
  940.                    excErrBufArgMaxTab, max_tab,
  941.                    excErrBufArgRightDelimiters, ",:;!?)]}",
  942.                    excErrBufArgLeftDelimiters, "([{",
  943.                    0);
  944.  
  945. #ifdef HAVE_TERMCAP
  946.     term_type = getenv ("TERM");
  947.     if (!term_type || tgetent (&str_buf[0], term_type) < 1)
  948.     termcap_failed = 1;
  949. #endif /* HAVE_TERMCAP */
  950.  
  951.     EXC_INSTALL_HANDLER (exc_any, exc_err_handler, NULL);
  952.     exc_install_callback (excRecoverCallback, cb, NULL);
  953.     
  954.     exc_err_inited = 1;
  955. }
  956.  
  957. /*
  958.  * Private
  959.  */
  960.  
  961. static excErrFormat* append_format (excErrBuf *buf, excErrFormatTag tag)
  962. {
  963.     /* Note: The return value is valid only until the next call */
  964.  
  965.     excErrFormat *fmt;
  966.  
  967.     if (buf->format_pos == buf->format_len)
  968.     {
  969.     buf->format_len *= 2;
  970.     if (buf->format_len == 0)
  971.         buf->format_len = 32;
  972.  
  973.     buf->format = (excErrFormat *) 
  974.         exc_realloc (buf->format, buf->format_len * sizeof (excErrBuf));
  975.     }
  976.  
  977.     fmt = &buf->format[buf->format_pos++];
  978.     fmt->tag = tag;
  979.     return fmt;
  980. }
  981.  
  982. static void make_buf_space (excErrBuf *buf, unsigned int req_len)
  983. {
  984.     /* Always make room for an trailing '\0' */
  985.  
  986.     if (buf->str_pos+req_len+1 > buf->str_len)
  987.     {
  988.     unsigned int inc = buf->str_pos+req_len+1-buf->str_len;
  989.  
  990.     if (buf->str_pos > inc)
  991.         inc = buf->str_pos;
  992.  
  993.     buf->str_len += inc;
  994.     buf->str = (char *) exc_realloc (buf->str, buf->str_len);
  995.     }
  996. }
  997.  
  998. static void append_nstr (excErrBuf *buf, const char *s, unsigned int n)
  999. {
  1000.     if (n > 0)
  1001.     {
  1002.     make_buf_space (buf, n);
  1003.     strncpy (&buf->str[buf->str_pos], s, n);
  1004.     buf->str_pos += n;
  1005.     }
  1006. }
  1007.  
  1008. #ifdef HAVE_TERMCAP
  1009. static void append_str (excErrBuf *buf, const char *s)
  1010. {
  1011.     append_nstr (buf, s, s ? strlen (s) : 0);
  1012. }
  1013. #endif
  1014.  
  1015. static void tab_to_cur (excErrBuf *buf)
  1016. {
  1017.     unsigned int delta;
  1018.  
  1019.     if (buf->cur_col >= buf->cur_tab)
  1020.     return;
  1021.  
  1022.     delta = buf->cur_tab - buf->cur_col;
  1023.  
  1024.     if (delta > 0)
  1025.     {
  1026.     clear_all_attr (buf);
  1027.     make_buf_space (buf, delta);
  1028.     memset (&buf->str[buf->str_pos], ' ', delta);
  1029.     buf->str_pos += delta;
  1030.     buf->cur_col = buf->cur_tab;
  1031.     reset_all_attr (buf);
  1032.     }
  1033. }
  1034.  
  1035. static void new_line (excErrBuf *buf)
  1036. {
  1037.     make_buf_space (buf, 1);
  1038.     buf->str[buf->str_pos++] = '\n';
  1039.     buf->cur_col = 0;
  1040.     buf->cur_tab = 0;
  1041. }
  1042.  
  1043. static void soft_new_line (excErrBuf *buf)
  1044. {
  1045.     make_buf_space (buf, 1);
  1046.     buf->str[buf->str_pos++] = '\n';
  1047.     buf->cur_col = 0;
  1048.     tab_to_cur (buf);
  1049. }
  1050.  
  1051. static int is_left_delim (excErrBuf *buf, char c)
  1052. {
  1053.     return (c != '\0' && strchr (buf->left_delim, c) != NULL);
  1054. }
  1055.  
  1056. static int is_right_delim (excErrBuf *buf, char c)
  1057. {
  1058.     return (c != '\0' && strchr (buf->right_delim, c) != NULL);
  1059. }
  1060.  
  1061. static int is_white (excErrBuf *buf, char c)
  1062. {
  1063.     return (c != '\0' && strchr (buf->whitespace, c) != NULL);
  1064. }
  1065.  
  1066. /*
  1067.  * sprintf buffers
  1068.  */
  1069.  
  1070. static void check_text_buf (textBuf *buf)
  1071. {
  1072.     if (buf->check_sum  != TEXT_BUF_CHECK_SUM)
  1073.     {
  1074.     fprintf (stderr, 
  1075.          "\nexc-err: detected corrupt memory in text buffer.\n");
  1076.     fprintf (stderr, 
  1077.          "exc-err: possible text buffer overflow (text > %d bytes).\n", 
  1078.          TEXT_BUF_SIZE);
  1079.     
  1080.     exc_panic();
  1081.     }
  1082. }
  1083.